package com.tyqx.hashchainbiz.controller.test;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.tyqx.hashchainbean.module.HashIndex;
import com.tyqx.hashchainbean.test.BaiWanTest;
import com.tyqx.hashchainbean.test.Test;
import com.tyqx.hashchainbean.test.TestYinYue;
import com.tyqx.hashchainbean.vo.BlockChainVO;
import com.tyqx.hashchainbean.vo.ImageVO;
import com.tyqx.hashchainbean.vo.MerkleRootVO;
import com.tyqx.hashchainbiz.service.TestService;
import com.tyqx.hashchainbiz.service.impl.ExcelService;
import com.tyqx.hashchainutil.common.BlockData;
import com.tyqx.hashchainutil.common.CryptoUtil;
import com.tyqx.hashchainutil.common.HashValueUtil;
import com.tyqx.hashchainutil.org.app.util.Util;
import hashchainutil.common.*;
import org.hyperledger.fabric.sdk.security.CryptoPrimitives;
import org.json.JSONObject;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/yinyue")
@CrossOrigin
public class YinYueTestController {

    @Resource
    private TestService testService;

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private ExcelService excelService;

    @RequestMapping(value = "/insertHashValueYinyue", method = RequestMethod.POST)
    @ResponseBody
    public Dto insertHashValueYinyue(@RequestParam("yinyueFile") MultipartFile yinyueFile, HttpServletRequest request) throws Exception {
        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .create();
        CryptoPrimitives suite = new CryptoPrimitives();
        suite.init();
        if (yinyueFile.isEmpty()) {
            return DtoUtil.returnFail("音乐文件不能为空", "100001");
        }

        String titleName=request.getParameter("titleName");
        String album=request.getParameter("album");
        String song=request.getParameter("song");
        String singer=request.getParameter("singer");
        String writingWords=request.getParameter("writingWords");
        String composition=request.getParameter("composition");
        String bianqu=request.getParameter("bianqu");
        String lssueDate=request.getParameter("lssueDate");
        String language=request.getParameter("language");

        TestYinYue testYinYue=new TestYinYue();
        testYinYue.setAlbum(album);
        testYinYue.setBianqu(bianqu);
        testYinYue.setComposition(composition);
        testYinYue.setLanguage(language);
        testYinYue.setTitleName(titleName);
        testYinYue.setSong(song);
        testYinYue.setSinger(singer);
        testYinYue.setWritingWords(writingWords);
        testYinYue.setLssueDate(lssueDate);

        String msg = "";
        String userName = "admin";

        try {
            if(EmptyUtils.isNotEmpty(testYinYue)){
                String contentType = yinyueFile.getContentType();

                byte[] imageBytes = yinyueFile.getBytes();

                BASE64Encoder base64Encoder = new BASE64Encoder();

                String base64EncoderImg = "data:" + contentType + ";base64," + base64Encoder.encode(imageBytes);

                String base64EncoderImg1 = base64EncoderImg.replaceAll("[\\s*\t\n\r]", "");

                byte[] hashBytes1 = suite.hash(base64EncoderImg1.getBytes());

                String attachmentHashValue = Base64.getEncoder().encodeToString(hashBytes1);

                ImageVO imageVO=new ImageVO();
                imageVO.setHashValue(attachmentHashValue);
                imageVO.setAttachment(base64EncoderImg1);
                mongoTemplate.insert(imageVO,"yinyueTest");
                testYinYue.setYinyueFileHashValue(attachmentHashValue);
            }
            String jsonString=gson.toJson(testYinYue);
            JSONObject jsonObject = new JSONObject(jsonString);
            JSONObject jsonObject1 = JsonUtil.getSortJson(jsonObject);
            String jsonString1 = jsonObject1.toString();

            byte[] hashBytes = suite.hash(jsonString1.getBytes());

            String hashValue = Base64.getEncoder().encodeToString(hashBytes);
            List<String> hashValueList = new ArrayList<>();
            hashValueList.add(hashValue);
            String rootHashValue = null;
            if (hashValueList.size() > 0) {
                List<List<String>> merkleTree = MerkleUtil.computeMerkleTree(hashValueList, suite);
                Integer merkleTreeSize = merkleTree.size();
                if (merkleTree.get(merkleTreeSize - 1).size() == 1) {
                    rootHashValue = merkleTree.get(merkleTreeSize - 1).get(0);
                } else {
                    throw new Exception("computeMerkleTree error");
                }
                HashIndex hashIndex = HashValueUtil.insertHashValue(rootHashValue, userName, mongoTemplate);
                String txId = hashIndex.getTxId();
                Long blockNum = hashIndex.getBlockNum();
                if (EmptyUtils.isNotEmpty(txId) && EmptyUtils.isNotEmpty(blockNum)) {
                    Test test = new Test();
                    test.setRootHashValue(rootHashValue);
                    test.setHashValue(hashValue);
                    test.setJsonString(jsonString1);
                    test.setTxId(txId);
                    test.setBlockNum(blockNum);
                    test.setTxTimeStamp(hashIndex.getTxTimeStamp());
                    Integer insertTestTableCount = testService.insertTest(test);

                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    //merkleRootVO.setHashValueList(hashValueList);
                    merkleRootVO.setMerkleTree(merkleTree);
                    merkleRootVO.setRootHashValue(rootHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");

                    if (insertTestTableCount > 0 && EmptyUtils.isNotEmpty(merkleRootVO.getId())) {
                        msg = "上链成功hashValue:" + hashValue;
                        return DtoUtil.returnSuccess(msg, test);
                    } else {
                        msg = "添加TestTable失败";
                        return DtoUtil.returnFail(msg, "100002");
                    }
                } else {
                    msg = "数据上链失败";
                    return DtoUtil.returnFail(msg, "100003");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return DtoUtil.returnFail("数据上链异常", "100001");
        }
        msg = "数据上链失败";
        return DtoUtil.returnFail(msg, "100004");
    }



    @RequestMapping(value = "/verifyDataYinyue", method = RequestMethod.POST,
            produces = "application/json")
    @ResponseBody
    public Dto verifyDataYinyue(@RequestParam("yinyueFile") MultipartFile yinyueFile, HttpServletRequest request) throws Exception {
        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .create();
        CryptoPrimitives suite = new CryptoPrimitives();
        suite.init();
        if (yinyueFile.isEmpty()) {
            return DtoUtil.returnFail("音乐文件不能为空", "100001");
        }

        String titleName=request.getParameter("titleName");
        String album=request.getParameter("album");
        String song=request.getParameter("song");
        String singer=request.getParameter("singer");
        String writingWords=request.getParameter("writingWords");
        String composition=request.getParameter("composition");
        String bianqu=request.getParameter("bianqu");
        String lssueDate=request.getParameter("lssueDate");
        String language=request.getParameter("language");

        TestYinYue testYinYue=new TestYinYue();
        testYinYue.setAlbum(album);
        testYinYue.setBianqu(bianqu);
        testYinYue.setComposition(composition);
        testYinYue.setLanguage(language);
        testYinYue.setTitleName(titleName);
        testYinYue.setSong(song);
        testYinYue.setSinger(singer);
        testYinYue.setWritingWords(writingWords);
        testYinYue.setLssueDate(lssueDate);

        String msg = "";
        Long orgIndex = 0L;
        String caType = "msp";
        String channelName = "hashchainchannel";
        try {
            if(EmptyUtils.isNotEmpty(testYinYue)){
                String contentType = yinyueFile.getContentType();

                byte[] imageBytes = yinyueFile.getBytes();

                BASE64Encoder base64Encoder = new BASE64Encoder();

                String base64EncoderImg = "data:" + contentType + ";base64," + base64Encoder.encode(imageBytes);

                String base64EncoderImg1 = base64EncoderImg.replaceAll("[\\s*\t\n\r]", "");

                byte[] hashBytes1 = suite.hash(base64EncoderImg1.getBytes());

                String attachmentHashValue = Base64.getEncoder().encodeToString(hashBytes1);

                testYinYue.setYinyueFileHashValue(attachmentHashValue);
            }
            String jsonString=gson.toJson(testYinYue);

            JSONObject jsonObject1 = new JSONObject(jsonString);
            jsonObject1 = JsonUtil.getSortJson(jsonObject1);
            String jsonObject2 = jsonObject1.toString();
            byte[] hashBytes = suite.hash(jsonObject2.getBytes());

            String hashValue = Base64.getEncoder().encodeToString(hashBytes);
            List<Test> testList = testService.getTestTable(hashValue);

            Test test = null;
            if (testList.size() > 0) {
                test = testList.get(0);
            } else {
                return DtoUtil.returnFail("未查询到该交易", "100002");
            }

            String userName = "admin";

            BlockData blockData = new BlockData();
            Map<String, String> map = blockData.getBlockByNum(test.getBlockNum(), channelName);
            String blockDataStr = map.get("blockDataStr");
            String blockNum = map.get("blockNum");
            JsonObject blockDataJson = new JsonParser().parse(blockDataStr).getAsJsonObject();
            JsonObject hashItemJson = blockData.getHashItemByTxId(blockDataJson, test.getTxId());
            String blockHashValue = hashItemJson.get("hashValue").toString();

            blockHashValue = blockHashValue.substring(blockHashValue.indexOf("\"") + 1, blockHashValue.lastIndexOf("\""));
            String blockHashSignature = hashItemJson.get("hashSignature").toString();
            blockHashSignature = blockHashSignature.substring(blockHashSignature.indexOf("\"") + 1, blockHashSignature.lastIndexOf("\""));

            Map<String, Object> pathMap = Util.readUserContext(caType, userName, orgIndex);
            Path clientCertPath = (Path) pathMap.get("clientCertPath");

            CryptoUtil cryptoUtil = new CryptoUtil();

            boolean flag = cryptoUtil.verify(clientCertPath, blockHashSignature, blockHashValue, mongoTemplate);

            String collectionName = "merkleRoot";

            List<String> hashValueList = new ArrayList<>();

            Criteria criteria = Criteria.where("rootHashValue").is(blockHashValue);
            Query query = Query.query(criteria);
            List<MerkleRootVO> MerkleRootVO = new ArrayList<>();
            MerkleRootVO = mongoTemplate.find(query, MerkleRootVO.class, collectionName);
            List<List<String>> merkleTree = new ArrayList<>();

            if (MerkleRootVO.size() > 0) {
                merkleTree = MerkleRootVO.get(0).getMerkleTree();
            } else {
                hashValueList = testService.getHashValueList(blockHashValue);
                merkleTree = MerkleUtil.computeMerkleTree(hashValueList, suite);
                if (merkleTree.size() > 0) {
                    MerkleRootVO merkleRootVO = new MerkleRootVO();
                    merkleRootVO.setMerkleTree(merkleTree);
                    merkleRootVO.setRootHashValue(blockHashValue);
                    mongoTemplate.insert(merkleRootVO, "merkleRoot");
                }
            }
            boolean varifyResult = MerkleUtil.verifyMerkleTree(merkleTree, hashValue, suite);

            if (varifyResult && flag) {
                BlockChainVO blockChainVO=new BlockChainVO();
                blockChainVO.setTxId(test.getTxId());
                blockChainVO.setHashValue(blockHashValue);
                blockChainVO.setBlockNum(blockNum);
                blockChainVO.setTxTimeStamp(test.getTxTimeStamp());
                blockChainVO.setUserName(userName);
                String successMsg = "验证区块链成功,数据位于区块:" + blockNum + ",数据签名人:" + userName;
                return DtoUtil.returnSuccess(msg, blockChainVO);
            } else {
                return DtoUtil.returnFail("验证区块链失败", "100001");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return DtoUtil.returnFail("验证区块链错误", "100003");
        }
    }

}
